

using System.Collections.Generic;
using System;
using System.Data;
namespace GeneralClasses
{

    //larger container class that holds a list of Days

    public class Schedule
    {
        #region variables
        //==============================================================================
        //                                                                     VARIABLES
        //==============================================================================
        private List<Day> days;
        private DateTime startDate;
        private DateTime endDate;
        private double grade; // whatever it got from the fitness test
        DatabaseConnection dbc;

        private List<Shift> shiftMasterList;
        private List<Person> personMasterList;
        private List<Department> departmentMasterList;
        private SchedulerPreferences schPref;
        #endregion

        #region PROPERTiES
        //==============================================================================
        //                                                                    PROPERTIES
        //==============================================================================
        public SchedulerPreferences SchedulerPreferences
        {
            get { return schPref; }
            set { schPref = value; }
        }
        /// <summary>
        /// master lists really only should be added and subtracted from via the subclasses instead of whatever contains the schedule
        /// </summary>
        public List<Department> DepartmentMasterList
        {
            get { return departmentMasterList; }
            set { departmentMasterList = value; }
        }
        public List<Person> PersonMasterList
        {
            get { return personMasterList; }
            set { personMasterList = value; }
        }
        public List<Shift> ShiftMasterList
        {
            get { return shiftMasterList; }
            set { shiftMasterList = value; }
        }

        /* public String ConnectionString
         {
             get { return dbc.ConnectionString; }
             set { dbc.ConnectionString = value; }
         }*/

        public double Grade
        {
            get { return grade; }
            set { grade = value; }
        }
        public DateTime EndDate
        {
            get { return endDate; }
            set { endDate = value; }
        }

        public DateTime StartDate
        {
            get { return startDate; }
            set
            {
                startDate = value;
                foreach (Day day in days)
                {

                }
            }
        }

        public List<Day> Days
        {
            get { return days; }
            set { days = value; }
        }
        #endregion

        #region constructor
        //==============================================================================
        //                                                                   CONSTRUCTOR
        //==============================================================================

        public Schedule()
        {
            this.startDate = new DateTime();
            this.endDate = new DateTime();
            initialize();
        }



        public Schedule(DateTime startDate, DateTime endDate)
        {
            this.startDate = startDate;
            this.endDate = endDate;
            initialize();
        }

        /// <summary>
        /// sets the default values and creates the list of days
        /// </summary>
        private void initialize()
        {
            schPref = new SchedulerPreferences();
            dbc = new DatabaseConnection();
            grade = -1; // if its -1 when you get it, that means it hasnt been graded yet. 

            //find the number of days from start to finish
            TimeSpan span = endDate.Subtract(startDate);

            ///initialize the master lists
            shiftMasterList = new List<Shift>();
            departmentMasterList = new List<Department>();
            departmentMasterList = Department.loadAllDepartments();
            personMasterList = new List<Person>();


            int numDays = (int)Math.Ceiling(span.TotalDays); // number of FULL days, also add in time for shifts, +1 for actually counting the last day

            days = new List<Day>();
            for (int i = 0; i < numDays; i++)
            {
                Day day = new Day();
                day.schedulePointer = this;
                day.DateTime = new DateTime(startDate.Year, startDate.Month, startDate.Day, startDate.Hour, startDate.Minute, startDate.Second);
                day.DateTime = day.DateTime.AddDays(i);
                day.Departments = departmentMasterList;
                days.Add(day);
            }

            //UTIL.sayDebug("number of days: " + days.Count);
        }

        #endregion

        #region private
        //==============================================================================
        //                                                                       PRIVATE
        //==============================================================================

        #endregion

        #region public
        //==============================================================================
        //                                                                        PUBLIC
        //==============================================================================

        public static String loadText(DateTime starttime, DateTime endtime)
        {
            String text = "";

            DatabaseConnection dbc = new DatabaseConnection();

            DataTable table = dbc.query("SELECT scheduletext FROM ScheduleText WHERE starttime='" + starttime + "' AND endtime='" + endtime + "'");
            if ((table != null) && (table.Rows.Count > 0))
            {
                foreach (DataRow row in table.Rows)
                {
                    text = text + (String)row["scheduletext"];
                }
            }

            return text;
        }

        public static String loadText()
        {
            String text = "";

            DatabaseConnection dbc = new DatabaseConnection();

            DataTable table = dbc.query("SELECT scheduletext FROM ScheduleText");
            if ((table != null) && (table.Rows.Count > 0))
            {
                foreach (DataRow row in table.Rows)
                {
                    text = text + (String)row["scheduletext"];
                }
            }

            return text;
        }

        /// <summary>
        /// saves all of the shift and personell data as an ntext in a special table
        /// </summary>
        /// <returns></returns>
        public int saveAsText()
        {
            try
            {
                string text = ""; // blank at start

                foreach (Day day in days)
                {
                    foreach (Department department in day.Departments)
                    {
                        foreach (Shift shift in department.shiftsOnThisDay(day.DateTime))
                        {
                            foreach (Person person in shift.Persons)
                            {
                                //person data
                                text = text + day.DateTime.DayOfWeek + "  "
                                    + department.Name + "  "
                                    + shift.StartTime + " to " + shift.EndTime + "   "
                                    + person.Username + "  " + person.FullName
                                    + Environment.NewLine;
                            }
                        }
                    }
                }

                dbc.executeNonQuery("INSERT INTO ScheduleText (starttime, endtime,scheduletext) VALUES ('" + startDate + "','" + endDate + "','" + text + "' )");

                return 0;
            }
            catch (Exception e)
            {
                UTIL.sayDebug(e);
                throw e;
            }
        }

        public int writeOutObjectTree(int uniqueCounter, int spaceIndex)
        {
            String spaces = "";
            Day oldDay = days[0];
            int dayCounter = 0;

            for (int i = 0; i < spaceIndex; i++) { spaces = spaces + "\t"; }
            UTIL.sayDebug("(" + uniqueCounter + ")" + spaces + this.GetType().Name);

            for (int i = 0; i < days.Count; i++)
            {
                for (int q = i + 1; q < days.Count - 1; q++)
                {

                }
            }
            foreach (Day day in days)
            {
                if (!day.Equals(oldDay))
                {
                    uniqueCounter++;
                    dayCounter++;
                    oldDay = day;
                }
                uniqueCounter = day.writeOutObjectTree(uniqueCounter, dayCounter, spaceIndex + 1);
            }

            return uniqueCounter;
        }

        public void setWithIndexes(List<int> personIndexes, int numberOfDepartments, int peoplePerDepartment, int numberOfShifts,
            int peoplePerShift, int peoplePerDay, int numberOfDays)
        {
            //make default values
            int dayIndex = 0;
            int shiftIndex = 0;
            int departmentIndex = 0;


            //go through every index
            for (int i = 0; i < personIndexes.Count; i++)
            {
                dayIndex = (int)((Math.Floor(i / (float)peoplePerDay)) % numberOfDays);
                shiftIndex = (int)((Math.Floor(i / (float)peoplePerShift)) % numberOfShifts);
                departmentIndex = (int)((Math.Floor(i / (float)peoplePerDepartment)) % numberOfDepartments);

                //UTIL.sayDebug("dayIndex: (" + dayIndex + ") departmentIndex: (" + departmentIndex + ") shiftIndex: (" + shiftIndex + ") personIndex: (" + personIndexes[i] + ")");
                UTIL.sayDebug("(" + dayIndex + "), (" + departmentIndex + "), (" + shiftIndex + "), (" + personIndexes[i] + ")");

                days[dayIndex].setWithIndex(personMasterList[personIndexes[i]], shiftIndex, departmentIndex);
            }
        }

        //just get schedule.ShiftMasterList.Count; 
        public int deprecated_countAllShiftsInDepartmentMasterList()
        {
            int sum = 0;
            foreach (Department d in departmentMasterList)
            {
                sum += d.Shifts.Count;
            }

            return sum;
        }

        /// <summary>
        /// loads the department to the master list and then returns it. 
        /// </summary>
        /// <param name="deid"></param>
        /// <returns></returns>
        public Department loadDepartmentToMasterList(int deid)
        {
            try
            {
                //see if the Department with that ID is already in there
                for (int i = 0; i <= departmentMasterList.Count - 1; i++)
                {
                    if (departmentMasterList[i].DepartmentID == deid)
                    {
                        return departmentMasterList[i];
                    }
                }

                //there wasnt, so make one, add it in, and send it out
                Department department = new Department();
                department.SchedulePointer = this;
                department.DepartmentID = deid;
                department.loadData();
                departmentMasterList.Add(department);
                return department;
            }
            catch (Exception e)
            {
                UTIL.sayDebug(e);
                throw e;
            }
        }

        /// <summary>
        /// adds the shift to the master list and then returns it
        /// </summary>
        /// <param name="shid"></param>
        /// <returns></returns>
        public Shift loadShiftToMasterList(int shid)
        {
            try
            {
                //see if the Shift with that ID is already in there
                for (int i = 0; i <= shiftMasterList.Count - 1; i++)
                {
                    if (shiftMasterList[i].ShiftID == shid)
                    {
                        return shiftMasterList[i];
                    }
                }

                //there wasnt, so make one, add it in, and send it out
                Shift shift = new Shift();
                shift.SchedulePointer = this;
                shift.ShiftID = shid;
                shift.loadData();
                shiftMasterList.Add(shift);
                return shift;
            }
            catch (Exception e)
            {
                UTIL.sayDebug(e);
                throw e;
            }
        }

        /// <summary>
        /// adds the person to the master list and then returns it
        /// </summary>
        /// <param name="perid"></param>
        /// <returns></returns>
        public Person loadPersonToMasterList(int perid)
        {
            try
            {
                //see if the person with that ID is already in there
                for (int i = 0; i <= personMasterList.Count - 1; i++)
                {
                    if (personMasterList[i].PERID == perid)
                    {
                        return personMasterList[i];
                    }
                }

                //there wasnt, so make one, add it in, and send it out
                Person person = new Person();
                person.PERID = perid;
                person.loadData();
                personMasterList.Add(person);
                return person;
            }
            catch (Exception e)
            {
                UTIL.sayDebug(e);
                //throw new Exception();
                throw e;
            }
        }

        /// <summary>
        /// makes a new schedule with 7 days, each for one day of the week, with 3 shifts in each department per each day. 
        /// </summary>
        /// <returns></returns>
        public static Schedule getWeeklyTemplateSchedule()
        {
            //make the default schedule starting at today
            DateTime startTime = DateTime.Today;
            DateTime endTime = DateTime.Today;
            endTime = endTime.AddDays(7);
            Schedule schedule = new Schedule(startTime, endTime);



            //for each day in the Week
            for (int dayIndex = 0; dayIndex < schedule.days.Count; dayIndex++)
            {

                //set day of the week to the appropriate one (ie, Monday, Tuesday, etc)
                schedule.Days[dayIndex].DayOfWeek = new DayOfWeek(dayIndex);

                //add in all the departments to this day
                schedule.Days[dayIndex].Departments = schedule.departmentMasterList;

                //set all the schedule pointers for this day
                schedule.Days[dayIndex].schedulePointer = schedule;

                //for each department, for each day.
                foreach (Department department in schedule.departmentMasterList)
                {
                    //set schedule pointer 
                    department.schedulePointer = schedule;

                    //add in the three shifts of this day
                    for (int shiftCount = 0; shiftCount < 3; shiftCount++)
                    {
                        Shift shift = new Shift(); // make the new shift
                        shift.schedulePointer = schedule;
                        DateTime start = schedule.days[dayIndex].DateTime; //start time is the start of this day.

                        shift.StartTime = new DateTime(start.Year, start.Month, start.Day, start.Hour, start.Minute, start.Second); //deep-copy startTime
                        // shift.StartTime = shift.StartTime.AddDays(dayIndex); // already set, dont need this
                        shift.StartTime = shift.StartTime.AddHours(8 * shiftCount); // adjust start time by 8 hour shifts

                        shift.EndTime = new DateTime(start.Year, start.Month, start.Day, start.Hour, start.Minute, start.Second); // deep copy endTime
                        shift.EndTime = shift.EndTime.AddDays(dayIndex);
                        shift.EndTime = shift.EndTime.AddHours(8 * (shiftCount + 1)); // adjust end time by 8 hour shifts, + 8 more hours

                        //if the end time is at midnight, then reduce by one minute
                        while (shift.EndTime.Day != shift.StartTime.Day)
                        {
                            shift.EndTime = shift.EndTime.AddMinutes(-1);
                        }

                        schedule.shiftMasterList.Add(shift); // add to master list
                        department.Shifts.Add(shift); // add to this department's list of shifts.

                    } // end shifts
                } // end departments
            } // end days
            //end schedule

            return schedule;
        }

        /// <summary>
        /// overloaded version that takes in several weeks
        /// </summary>
        /// <param name="numOfWeeks">how many weeks to schedule for</param>
        /// <returns></returns>
        public static Schedule getWeeklyTemplateSchedule(int numOfWeeks)
        {
            //make sure weeks isnt retarded
            if (numOfWeeks < 1) { numOfWeeks = 1; }

            //make the default schedule starting at today
            DateTime startTime = DateTime.Today;
            DateTime endTime = DateTime.Today;
            endTime = endTime.AddDays(7 * numOfWeeks);
            Schedule schedule = new Schedule(startTime, endTime);



            //for each day in the Week
            for (int dayIndex = 0; dayIndex < schedule.days.Count; dayIndex++)
            {

                //set day of the week to the appropriate one (ie, Monday, Tuesday, etc)
                schedule.Days[dayIndex].DayOfWeek = new DayOfWeek(dayIndex);

                //add in all the departments to this day
                schedule.Days[dayIndex].Departments = schedule.departmentMasterList;

                //set all the schedule pointers for this day
                schedule.Days[dayIndex].schedulePointer = schedule;

                //for each department, for each day.
                foreach (Department department in schedule.departmentMasterList)
                {
                    //set schedule pointer 
                    department.schedulePointer = schedule;

                    //add in the three shifts of this day
                    for (int shiftCount = 0; shiftCount < 3; shiftCount++)
                    {
                        Shift shift = new Shift(); // make the new shift
                        shift.schedulePointer = schedule;
                        DateTime start = schedule.days[dayIndex].DateTime; //start time is the start of this day.

                        shift.StartTime = new DateTime(start.Year, start.Month, start.Day, start.Hour, start.Minute, start.Second); //deep-copy startTime
                        // shift.StartTime = shift.StartTime.AddDays(dayIndex); // already set, dont need this
                        shift.StartTime = shift.StartTime.AddHours(8 * shiftCount); // adjust start time by 8 hour shifts

                        shift.EndTime = new DateTime(start.Year, start.Month, start.Day, start.Hour, start.Minute, start.Second); // deep copy endTime
                        shift.EndTime = shift.EndTime.AddDays(dayIndex);
                        shift.EndTime = shift.EndTime.AddHours(8 * (shiftCount + 1)); // adjust end time by 8 hour shifts, + 8 more hours

                        //if the end time is at midnight, then reduce by one minute
                        while (shift.EndTime.Day != shift.StartTime.Day)
                        {
                            shift.EndTime = shift.EndTime.AddMinutes(-1);
                        }

                        schedule.shiftMasterList.Add(shift); // add to master list
                        department.Shifts.Add(shift); // add to this department's list of shifts.

                    } // end shifts
                } // end departments
            } // end days
            //end schedule

            return schedule;
        }


        /// <summary>
        /// Throws out all the information inside THIS schedule object that does not pertain 
        /// to the given person. 
        /// </summary>
        /// <param name="person">Person instance that this schedule to personalize for</param>
        public void personalizeSchedule(Person person)
        {
            //personalize all days
            foreach (Day d in days)
            {
                d.personalize(person); // this is the new one
            }
        }

        /// <summary>
        /// removes all the data in this schedule from the database
        /// WARNING: THIS CANNOT BE UNDONE
        ///    X         X
        ///    \\_______//
        ///     /       \
        ///    |  X   X  |
        ///     \   A   /
        ///    // IMMMI \\
        ///    X  IWWWI  X
        /// the function and all similar functions that are called by this are designed so that if there is 
        /// relation between an object that needs to be deleted and another object, it will not be deleted. 
        /// </summary>
        public int removeData()
        {
            try
            {
                int errors = 0;
                foreach (Day d in days)
                {
                    errors += d.removeData(); // if error, they return -1
                }

                //since the schedule itself isnt in the database, it wont return it's own -1 error
                return -errors;
            }
            catch (Exception e)
            {
                UTIL.sayDebug(e);
                throw e;
            }
        }

        /// <summary>
        /// Saves all the data inside this schedule to the database.
        /// Doesn't put in redundant information, but each class lower on the hierarchy will handle their own stuff.
        /// Returns the number of days that arent in the database. 
        /// </summary>
        public int saveData()
        {
            try
            {
                int errors = 0;

                //save all teh days
                foreach (Day d in days)
                {
                    if (d.saveData() == -1) { errors += 1; }
                }


                //save all the departments
                foreach (Department department in departmentMasterList)
                {
                    department.saveData();
                    //this also saves all the shifts that are inside them through hierarchy
                }

                //save all people
                foreach (Person person in personMasterList)
                {
                    person.saveData();
                }

                return errors;
            }
            catch (Exception e)
            {
                UTIL.sayDebug(e);
                throw e;
            }
        }

        /// <summary>
        /// used when you want to load a schedule from the database, it calls all teh days to load,
        /// and it trickles down through the class hierarchy
        /// </summary>
        public void LoadData()
        {
            try
            {
                //load the schedule preferences


                //load all the departments
                departmentMasterList = Department.loadAllDepartments();

                //load all the days in the scope of this schedule
                foreach (Day d in days)
                {
                    d.loadData();
                }

                //load all the shifts in all the departments
                foreach (Department department in departmentMasterList)
                {
                    department.loadData();
                }
            }
            catch (Exception e)
            {
                UTIL.sayDebug(e);
                throw e;
            }
        }

        #region deprecated
        /*public Day getDay(DateTime d){
            foreach(Day day in days){
                if(day.DateTime.Equals(d))
            }
        }*/
        #endregion

        #endregion
    } // end class


} //end namespace